#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "../src/list.h"


// printf("%-64s", "\x1b[33m" # func "\x1b[0m");
#define test(func) \
    printf("... %-64s", "\033[1;33m" #func "\033[0m");      \
    test_##func();                              \
    printf("\033[1;32m ok \033[0m\n")



void elem_free(void *val)
{(void)val;}

int elem_cmp(const void *a, const void *b)
{
    return strncmp(a, b, strlen(a));
}

void *elem_copy(void *src, void *match)
{
    if (match) {
        if (strncmp(match, src, strlen(match)))
            return NULL;
    }
    return strdup(src);
}


void test_list_new()
{
    list_t *l = NULL;
    l = list_new();
    assert(l != NULL);
    assert(l->cnt == 0);
    assert(l->first == NULL);
    assert(l->first == l->last);

    list_delete(l);
}


void test_list_insert()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";

    // a
    list_insert_rignt(l, l->first, a);
    assert(l->first->val == a);
    assert(l->cnt == 1);

    // b->a
    list_insert_left(l, l->first, b);
    // b->c->a
    list_insert_rignt(l, l->first, c);
    assert(l->cnt == 3);

    assert(l->first->val == b);
    assert(l->last->val == a);
    assert(l->first->next->val == c);
    assert(l->last->prev->val == c);
    assert(l->first->prev == NULL);
    assert(l->last->next == NULL);

    list_delete(l);
}


void test_list_lpush()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";
    char *d = "aoo";

    list_lpush(l, a);  // a
    list_lpush(l, b);  // b->a
    list_lpush(l, c);  // c->b->a
    list_lpush(l, d);  // d->c->b->a
    assert(l->cnt == 4);
    assert(l->first->val == d);
    assert(l->first->next->val == c);
    assert(l->first->next->next->val == b);
    assert(l->first->next->next->next->val == a);
    assert(l->last->val == a);
    assert(l->last->prev->val == b);
    assert(l->last->prev->prev->val == c);
    assert(l->last->prev->prev->prev->val == d);

    list_delete(l);
}


void test_list_rpush()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";
    char *d = "aoo";

    list_rpush(l, a);  // a
    list_rpush(l, b);  // a->b
    list_rpush(l, c);  // a->b->c
    list_rpush(l, d);  // a->b->c->d
    assert(l->cnt == 4);
    assert(l->first->val == a);
    assert(l->first->next->val == b);
    assert(l->first->next->next->val == c);
    assert(l->first->next->next->next->val == d);
    assert(l->last->val == d);
    assert(l->last->prev->val == c);
    assert(l->last->prev->prev->val == b);
    assert(l->last->prev->prev->prev->val == a);

    list_delete(l);

}


void test_list_iterator()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";
    char *d = "aoo";

    list_rpush(l, a);  // a
    list_rpush(l, b);  // a->b
    list_rpush(l, c);  // a->b->c
    list_rpush(l, d);  // a->b->c->d

    int i = 0;
    list_node_t *n = NULL; // 必须初始化

    while(list_iterator_next(l, &n)) {
        if (i == 0)
            assert(n->val == a);
        else if (i == 1)
            assert(n->val == b);
        else if (i == 2)
            assert(n->val == c);
        else
            assert(n->val == d);
        i++;
    }

    i = 0;
    while(list_iterator_prev(l, &n)) {
        if (i == 0)
            assert(n->val == d);
        else if (i == 1)
            assert(n->val == c);
        else if (i == 2)
            assert(n->val == b);
        else
            assert(n->val == a);
        i++;
    }

    list_delete(l);
}


void test_list_find()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";
    char *d = "aoo";

    list_rpush(l, a);  // a
    list_rpush(l, b);  // a->b
    list_rpush(l, c);  // a->b->c
    list_rpush(l, d);  // a->b->c->d

    // use default
    assert(list_find(l, a)->val == a);
    assert(list_find(l, b)->val == b);
    assert(list_find(l, c)->val == c);
    assert(list_find(l, d)->val == d);

    assert(list_match_find(l, a, elem_cmp)->val == a);
    assert(list_match_find(l, b, elem_cmp)->val == b);
    assert(list_match_find(l, c, elem_cmp)->val == c);
    assert(list_match_find(l, d, elem_cmp)->val == d);

    list_set_cmp_func(l, elem_cmp);
    // use you set cmp func
    assert(list_find(l, a)->val == a);
    assert(list_find(l, b)->val == b);
    assert(list_find(l, c)->val == c);
    assert(list_find(l, d)->val == d);

    list_delete(l);
}


void test_list_at()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";
    char *d = "aoo";

    list_rpush(l, a);  // a
    list_rpush(l, b);  // a->b
    list_rpush(l, c);  // a->b->c
    list_rpush(l, d);  // a->b->c->d

    assert(list_at(l, 0)->val == a);
    assert(list_at(l, 1)->val == b);
    assert(list_at(l, 2)->val == c);
    assert(list_at(l, 3)->val == d);
    assert(list_at(l, -1)->val == d);
    assert(list_at(l, -2)->val == c);
    assert(list_at(l, -3)->val == b);
    assert(list_at(l, -4)->val == a);

    list_delete(l);
}


void test_list_remove()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";
    char *d = "aoo";

    list_rpush(l, a);  // a
    list_rpush(l, b);  // a->b
    list_rpush(l, c);  // a->b->c
    list_rpush(l, d);  // a->b->c->d

    assert(list_at(l, 0)->val == a);
    assert(list_at(l, 1)->val == b);
    assert(list_at(l, 2)->val == c);
    assert(list_at(l, 3)->val == d);
    assert(l->cnt == 4);

    list_remove(l, a);
    list_remove(l, c);
    assert(list_at(l, 0)->val == b);
    assert(list_at(l, 1)->val == d);
    assert(l->cnt == 2);

    list_remove(l, b);
    list_remove(l, d);
    assert(l->cnt == 0);

    assert(list_remove(l, a) == -1);
    assert(list_remove(l, b) == -1);
    assert(list_remove(l, c) == -1);
    assert(list_remove(l, d) == -1);

    list_delete(l);
}


void test_list_unlink()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";
    char *d = "aoo";

    list_rpush(l, a);  // a
    list_rpush(l, b);  // a->b
    list_rpush(l, c);  // a->b->c
    list_rpush(l, d);  // a->b->c->d
    assert(l->cnt == 4);

    list_node_t *fd = list_find(l, d);
    if (fd)
        list_unlink(l, fd);
    assert(l->cnt == 3);
    assert(l->last->val == c);
    assert(list_remove(l, d) == -1);

    list_node_t *fa = list_find(l, a);
    if (fa)
        list_unlink(l, fa);
    assert(l->cnt == 2);
    assert(l->first->val == b);
    assert(list_remove(l, a) == -1);

    list_unlink(l, l->last);
    assert(l->cnt == 1);
    assert(l->last->val == b);
    assert(list_remove(l, c) == -1);

    list_unlink(l, l->first);
    assert(l->cnt == 0);
    assert(l->first == NULL);
    assert(l->last == NULL);
    assert(list_remove(l, b) == -1);

    list_delete(l);
}

void test_list_rpop()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";
    char *d = "aoo";

    list_rpush(l, a);  // a
    list_rpush(l, b);  // a->b
    list_rpush(l, c);  // a->b->c
    list_rpush(l, d);  // a->b->c->d
    assert(l->cnt == 4);

    list_node_t *n ;
    n = list_rpop(l);
    assert(l->cnt == 3);
    assert(n->val == d);
    assert(list_find(l, d) == NULL);
    free(n);

    n = list_rpop(l);
    assert(l->cnt == 2);
    assert(n->val == c);
    assert(list_find(l, c) == NULL);
    free(n);

    n = list_rpop(l);
    assert(l->cnt == 1);
    assert(n->val == b);
    assert(list_find(l, b) == NULL);
    free(n);

    n = list_rpop(l);
    assert(l->cnt == 0);
    assert(n->val == a);
    assert(list_find(l, a) == NULL);
    free(n);

    list_delete(l);
}

void test_list_lpop()
{
    list_t *l = list_new();
    assert(l != NULL);
    list_set_free_func(l, elem_free);

    char *a = "foo";
    char *b = "tar";
    char *c = "har";
    char *d = "aoo";

    list_rpush(l, a);  // a
    list_rpush(l, b);  // a->b
    list_rpush(l, c);  // a->b->c
    list_rpush(l, d);  // a->b->c->d
    assert(l->cnt == 4);

    list_node_t *n ;
    n = list_lpop(l);
    assert(l->cnt == 3);
    assert(n->val == a);
    assert(list_find(l, a) == NULL);
    free(n);

    n = list_lpop(l);
    assert(l->cnt == 2);
    assert(n->val == b);
    assert(list_find(l, b) == NULL);
    free(n);

    n = list_lpop(l);
    assert(l->cnt == 1);
    assert(n->val == c);
    assert(list_find(l, c) == NULL);
    free(n);

    n = list_lpop(l);
    assert(l->cnt == 0);
    assert(n->val == d);
    assert(list_find(l, d) == NULL);
    free(n);

    list_delete(l);

}


int main(int argc, char *argv[])
{
    test(list_new);
    test(list_insert);
    test(list_lpush);
    test(list_rpush);
    test(list_iterator);
    test(list_find);
    test(list_at);
    test(list_remove);
    test(list_unlink);
    test(list_rpop);
    test(list_lpop);

    puts("... \x1b[32m100%\x1b[0m\n");
    return 0;
}

